package org.gfix.binding;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.math.BigDecimal;

import org.gfix.binding.ClassFixBindingDescription;
import org.gfix.binding.FixBindingException;
import org.gfix.binding.FixBindingMetaData;
import org.gfix.binding.MessageSink;
import org.gfix.types.FixVersion;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(JMock.class)
public class BasicObjectToFixTest {

	protected Mockery context = new JUnit4Mockery();

	@Test(expected=FixBindingException.class)
	public void testClassFixBindingDescriptionError() throws Exception {
		new ClassFixBindingDescription(
				MultipleFieldsToSameTagType.class);
	}
	
	@Test
	public void testClassFixBindingDescription() throws Exception {
		ClassFixBindingDescription desc = new ClassFixBindingDescription(
				SimpleBoundType.class);
		FixBindingMetaData fbmd = desc.getMetadata(FixVersion.FIX_40);
		assertNotNull(fbmd);
		assertEquals(12, fbmd.getMappedFields());

		// now check we can push values into the object.
		SimpleBoundType type = new SimpleBoundType();
		type.setBooleanType(Boolean.TRUE);
		type.setCharacterType('G');
		type.setByteType((byte) 23);
		type.setShortType((short) 34);
		type.setIntegerType(345);
		type.setLongType(3467L);
		type.setFloatType(345.5678f);
		type.setDoubleType(345678.567);
		type.setStringType("This is FIX at its best");
		type.setBigDecimalType(new BigDecimal("123.678965"));
		type.setVersion(FixVersion.FIX_44);
		type.setSimpleEnumType(SimpleEnumType.HELLO);

		final MessageSink sink = context.mock(MessageSink.class);
		// set expectations.
		context.checking(new Expectations() {
			{
				oneOf(sink).writeTag(with(61), with("true"));
				oneOf(sink).writeTag(with(62), with("G"));
				oneOf(sink).writeTag(with(63), with("23"));
				oneOf(sink).writeTag(with(64), with("34"));
				oneOf(sink).writeTag(with(65), with("345"));
				oneOf(sink).writeTag(with(66), with("3467"));
				oneOf(sink).writeTag(with(67), with("345.5678"));
				oneOf(sink).writeTag(with(68), with("345678.567"));
				oneOf(sink).writeTag(with(69), with("This is FIX at its best"));
				oneOf(sink).writeTag(with(70), with("123.678965"));
				oneOf(sink).writeTag(with(71), with("FIX.4.4"));
				oneOf(sink).writeTag(with(72), with("HELLO"));
			}
		});

		fbmd.serialise(type, sink);
	}

	@Test
	public void testOrderingOnSerialise() throws Exception {
		ClassFixBindingDescription desc = new ClassFixBindingDescription(
				OrderingBoundType.class);
		FixBindingMetaData fbmd = desc.getMetadata(FixVersion.FIX_40);
		assertNotNull(fbmd);
		OrderingBoundType type = new OrderingBoundType();
		type.setName("name");
		type.setName1("name1");
		type.setName2("name2");
		type.setName3("name3");
		final MessageSink sink = context.mock(MessageSink.class);
		// set expectations.
		final Sequence sequence = context.sequence("messageSequence");
		context.checking(new Expectations() {
			{
				oneOf(sink).writeTag(with(90), with("name3")); inSequence(sequence);
				oneOf(sink).writeTag(with(80), with("name2")); inSequence(sequence);
				oneOf(sink).writeTag(with(70), with("name1")); inSequence(sequence);
				oneOf(sink).writeTag(with(60), with("name")); inSequence(sequence);
			}
		});

		fbmd.serialise(type, sink);
	}

	@Test
	public void testSimpleConverter() throws Exception
	{
		ClassFixBindingDescription desc = new ClassFixBindingDescription(
				SimpleConverterType.class);
		FixBindingMetaData fbmd = desc.getMetadata(FixVersion.FIX_40);
		assertNotNull(fbmd);
		SimpleConverterType output = new SimpleConverterType();
		output.setName("lowerName");
		final MessageSink sink = context.mock(MessageSink.class);

		context.checking(new Expectations() {
			{
				oneOf(sink).writeTag(with(72), with("LOWERNAME"));
			}
		});
		fbmd.serialise(output, sink);
	}
	
	@Test
	public void testSimpleNested() throws Exception
	{
		ClassFixBindingDescription desc = new ClassFixBindingDescription(
				SimpleNestedBoundType.class);
		FixBindingMetaData fbmd = desc.getMetadata(FixVersion.FIX_40);
		assertNotNull(fbmd);
		SimpleNestedBoundType output = new SimpleNestedBoundType();
		output.setName("lowerName");
		output.setSubObject(new SimpleConverterType());
		output.getSubObject().setName("lowerNameTwo");
		final MessageSink sink = context.mock(MessageSink.class);

		context.checking(new Expectations() {
			{
				oneOf(sink).writeTag(with(70), with("lowerName"));
				oneOf(sink).writeTag(with(72), with("LOWERNAMETWO"));
			}
		});
		fbmd.serialise(output, sink);
	}
	
	
}